package redisv9ex

import (
	"context"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/gobase/factory"
	"gitee.com/ymofen/gobase/subpub"
	"gitee.com/ymofen/sputils"
	"github.com/redis/go-redis/v9"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type RedisSpOpera struct {
	startedflag int32

	startlk sync.RWMutex

	ctx     context.Context
	opera   *RedisOperator
	cmdRef  *gobase.ObjectInnerRef
	connstr string

	ex      *sputils.Subscribe
	closewg sync.WaitGroup

	statusText string
}

type pubArg struct {
	operaType string
	args      []string
	exp       time.Duration
}

func (this *RedisSpOpera) checkConnect() error {
	this.cmdRef = DefaultRedisClient.CheckGetRef(this.connstr)
	this.opera = NewRedisOperator(this.cmdRef.GetObject().(redis.Cmdable))
	return nil
}

func (this *RedisSpOpera) CheckStart() error {
	this.startlk.Lock()
	defer this.startlk.Unlock()
	if this.startedflag == 1 {
		return nil
	}
	defer func() {
		this.startedflag = 1
	}()

	this.statusText = fmt.Sprintf("[%s]初始化RedisClient", gobase.NowString())
	err := this.checkConnect()
	if err != nil {
		this.statusText = fmt.Sprintf("[%s]初始化RedisClient异常:%s", gobase.NowString(), err.Error())
		this.ex.Pub(sputils.SpEvtNotify, 0, sputils.SpTypeError, err.Error())
	} else {
		this.closewg.Add(1)
	}
	return err
}

func (this *RedisSpOpera) closeRedis() {
	if this.cmdRef != nil {
		this.cmdRef.ReleaseRef()
		this.opera.Close()
	}
}

func (this *RedisSpOpera) Ping() error {
	clt := this.opera
	if clt == nil {
		return gobase.ErrInvalidObject
	}
	return this.opera.Ping()
}

func (this *RedisSpOpera) Close() error {
	this.startlk.Lock()
	defer this.startlk.Unlock()
	if this.startedflag == 1 {
		this.startedflag = 0
		this.closeRedis()
	}
	atomic.AddInt32(&redisClientN, -1)
	return nil
}

func (this *RedisSpOpera) WaitDone() {
	this.closewg.Wait()
}

// 1: topic, args[0].([]byte/string)
// 1: topic, args[0].([]byte/string), args[1].(pubArg)
// 没办法获取到发送成功的数量, 1:表示发送成功了
func (this *RedisSpOpera) Pub(topic string, max int, args ...interface{}) int {
	clt := this.cmdRef
	if clt == nil {
		this.ex.Pub(sputils.SpEvtNotify, 0, sputils.SpTypeError, fmt.Errorf("pub:%s, err:%s", topic, "redis is nil"))
		return -1
	}

	if len(args) == 2 {
		if pa, ok := args[1].(*pubArg); ok {
			if pa.operaType == "hset" {
				_, err := this.opera.HSet(pa.args[0], topic, gobase.GetStrValue(args[0], ""))
				if err != nil {
					return -1
				}
				return 1
			} else if pa.operaType == "set" {
				err := this.opera.SetKeyValue(topic, gobase.GetStrValue(args[0], ""), pa.exp)
				if err != nil {
					return -1
				}
				return 1
			}
		}
	}

	err := this.opera.SetKeyValue(topic, gobase.GetStrValue(args[0], ""), 0)
	if err != nil {
		return 1
	}

	return int(1)
}

func (this *RedisSpOpera) Sub(id, channel string, cb subpub.SubFunc) {
	r := this.ex.Sub(id, channel, cb)
	if r == 1 {
		if strings.HasPrefix(channel, sputils.SpEvtPrefix) {
			return
		}
	}
}

func (this *RedisSpOpera) Unsub(id, channel string) bool {
	r, _ := this.ex.Unsub(id, channel)
	if r == 0 {
		if strings.HasPrefix(channel, sputils.SpEvtPrefix) {
			return true
		}
	}
	return true
}

func (this *RedisSpOpera) GetStatus(args ...interface{}) string {
	return fmt.Sprintf("%s,%s", this.connstr, this.statusText)
}

func (this *RedisSpOpera) ConvertArgs(action int, confMap gobase.StrMap) interface{} {
	var arg pubArg
	arg.operaType = confMap["type"]
	if len(arg.operaType) == 0 {
		return nil
	}
	if arg.operaType == "hset" {
		arg.args = append(arg.args, confMap["key"])
	} else if arg.operaType == "set" {
		arg.exp = gobase.ParseDurationEx(confMap["exp"], 0)
	}
	return &arg
}

var (
	DefaultRedisSpOpera = gobase.NewConnectObjectRefStorage(newRedisSpOpera)
)

// connstr=&user=&pass=&conrand=&topic=
func newRedisSpOpera(connstr string) interface{} {
	conf := gobase.NewStrMap()
	conf.URLFormDecode(connstr)
	node := &RedisSpOpera{ex: sputils.NewSubscribe()}
	node.connstr = connstr
	atomic.AddInt32(&redisClientN, 1)
	return node
}

func checkGetRedisSpOpera(args ...interface{}) (rval interface{}, err error) {
	if len(args) == 0 {
		err = fmt.Errorf("无效的参数")
		return
	}

	confMap, ok := args[0].(gobase.StrMap)
	if !ok {
		if str, ok1 := args[0].(string); ok1 {
			confMap = gobase.NewStrMap()
			confMap.URLFormDecode(str)
		}
	}
	if confMap == nil || len(confMap) == 0 {
		return nil, fmt.Errorf("Acquisition failed! invalid args!")
	}
	if _, ok0 := confMap["addr"]; !ok0 {
		if v, ok1 := confMap["connstr"]; ok1 {
			confMap["addr"] = v
		}
	}

	s := confMap.URLEncode0WithKeys("addr", "pass", "user", "clientid", "connrand", "cluster")
	rval = DefaultRedisSpOpera.CheckGetRef(s)
	return
}

func init() {
	// 返回一个objectRef, 返回的objectRef必须实现isubpub,iclose接口
	factory.RegisterCreateFunc("sp.redisopera", checkGetRedisSpOpera)

}
